package dkbta;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

import java.net.Socket;
import java.net.UnknownHostException;

import java.util.concurrent.LinkedBlockingQueue;

import org.apache.log4j.Logger;


/**
 * 
 * @author Maor Guetta 
 *
 */
public class ConnectionHandler extends Thread{

	private LinkedBlockingQueue<String> m_plansQueue;
	private boolean m_beenCanceled;
	private volatile boolean m_isReady;
	private static Logger s_logger;
	private String m_serverIp;
	private int m_serverPort;
	private Socket m_socket;
	private PrintWriter m_out;
	private BufferedReader m_in;				
	private boolean m_isConnected = false;
	private int m_id;
	private String m_leftOver;

	public ConnectionHandler(LinkedBlockingQueue<String> plansQueue){
		super("ConnectionHandler");
		s_logger = Logger.getLogger(ConnectionHandler.class);
		m_isReady = false;
		m_leftOver = "";
		m_plansQueue = plansQueue;
		m_serverIp = Config.getString("SERVER_IP","123");
		m_serverPort = Config.getInt("UNITS_REGISTRY_PORT", 9000);
		connect();
		receiveId();
		m_beenCanceled = false;
	}

	public void run(){
		while (true){ 
			String msg = receiveMsg();
			switch (msgType(msg)) {
			case 0:
				//not implemented. no need
//				sendResults(Boolean.toString(getMyState()));
				s_logger.info("Get testState message from server. State :" + getMyState());
				break;
			case 1:
				s_logger.info("Get Plan from server");
				addPlan(msg);
				break;
			case 2:
				s_logger.info("Get Plan cancellation from server");
				cancelPlan();
				break;

			default:
				s_logger.warn("Unknown message type received from server");
				break;
			} 
		}
	}

	private void cancelPlan(){
		m_beenCanceled = true;
	}

	public synchronized void setMyState(boolean state){
		m_isReady = state;
	}


	private synchronized boolean getMyState(){
		return m_isReady;
	}

	/*
	 * Gets a "plan#...." message and adds it to the plan's Queue.
	 */
	private void addPlan(String msg){
		//
		m_isReady = false;
		try {
			// remove the characters "plan#" from the message
			msg = msg.substring(5, msg.length());
			// add the plan to the Queue
			m_plansQueue.put(msg);
		} catch (Exception e) {
			s_logger.error("Can't add a plan to the Plan's Queue");
		}
	}

	//	public int getUnitId(){
	//		return m_id;
	//	}

	/**
	 * establishing a connectiong with the coordinator.
	 *
	 */
	private void connect(){
		s_logger.info("Connecting to " + m_serverIp + ":" + m_serverPort);
		try {					
			m_socket = new Socket(m_serverIp, m_serverPort);
			m_out = new PrintWriter(m_socket.getOutputStream(), true);
			m_in = new BufferedReader(new InputStreamReader(m_socket.getInputStream()));				
			m_isConnected = true;
		} catch (UnknownHostException e) {
			s_logger.warn("Don't know about host " + m_serverIp);		            
		} catch (IOException e) {
			s_logger.warn("Couldn't get I/O for the connection to: " + 
					m_serverIp);
		} catch (Exception e) {
			s_logger.warn("Failed to get server ip and port. Try again");
		}
		finally{
			if(!m_isConnected){
				System.exit(-1);
			}
		}
		s_logger.info("Connected ok");
	}

	private void receiveId(){
		String ids = receiveMsg();
		try{
			m_id = Integer.parseInt(ids);
			s_logger.info("Get id from server. #" + m_id + ".");
		}
		catch (NumberFormatException e) {
			s_logger.fatal("Illegal string format. Failed to get unitId!!!");
			System.exit(-1);
		}
	}

	public synchronized void sendResults(String results){
		m_out.print(results + Config.FRAME);
		m_out.flush();
		s_logger.info("Message sent to server. " + results + ".");
	}

	/**
	 * determines the message type:
	 * 0 - for "getState" message, 1 - for "plan" message 
	 * and 2 - for "cancel" message
	 * @param msg
	 * @return
	 */
	private int msgType(String msg){
		int delimiterInd = msg.indexOf("#");
		String typeStr = msg.substring(0,delimiterInd);
		s_logger.info("Accepted a " + typeStr + " Message from server");
		if (typeStr.compareTo("getState")==0){
			return 0;
		} else if (typeStr.compareTo("plan")==0){
			return 1;
		} else if (typeStr.compareTo("cancel")==0){
			return 2;
		} else {
			s_logger.warn("Unknown message type accepted from Server");
			return -1;
		}
	}
	/**
	 * wating to receive a message from the server
	 * @return
	 */
	//	private String receiveMsg(){
	//		String msg = null;
	//		try {
	//			msg = m_in.readLine();			
	//		} catch (IOException e) {
	//			s_logger.fatal("The connection with server failed!!!");
	//			System.exit(-1);
	//		}		
	//		return msg;
	//	}

	public String receiveMsg() {
		String ans = m_leftOver;
		String toReturn = null;
		boolean readMore = true;
		final char[] charBuffer = new char[10000];
		final StringBuilder sb = new StringBuilder();
		int k = 0;
		while(readMore){			
			try {
				k = m_in.read(charBuffer);
			} catch (IOException e) {
				s_logger.fatal("The connection with server failed!!!");
				System.exit(-1);
			}			
			if(k == -1){
				m_isConnected = false;				
			}
			sb.append(charBuffer,0,k);
			ans = ans + sb.toString();
			if(ans.contains(Config.FRAME)){
				readMore = false;
				String left = ans.substring(ans.indexOf(Config.FRAME) + 
						Config.FRAME.length(),ans.length());
				m_leftOver = left;
				ans = ans.substring(0,ans.indexOf(Config.FRAME));
				toReturn = ans.replace(Config.FRAME,"");
			}        
		}
		return toReturn ;
	}	


	public int getUnitId() {
		return m_id;
	}
}